using SharpDX;
using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;

namespace Scenario_Editor.Exporter
{
    /// <summary>
    /// wavefront Object Mesh
    /// </summary>
    internal class WavefrontObjects
    {
        #region Private Fields

        //UVCoordinates are assigned to faces not to Vertices
        private List<Polygon> faces;

        private List<string> materialNames;
        private List<Vector2> uvCoordinates;
        private List<Vector3> vectors;

        #endregion Private Fields

        #region Internal Constructors

        /// <summary>
        /// Creates a new Wavefront Mesh
        /// </summary>
        /// <param name="vertices">a list of verrtices</param>
        /// <param name="uvCoordinates">a list of uv-coordinates</param>
        /// <param name="faces">the indices for  the polygons</param>
        /// <param name="materialNames">the materialnames</param>
        internal WavefrontObjects(List<Vector3> vertices, List<Vector2> uvCoordinates, List<Polygon> faces, List<string> materialNames)
        {
            this.vectors = vertices;
            this.uvCoordinates = uvCoordinates;
            this.faces = faces;
            this.materialNames = materialNames;
        }

        #endregion Internal Constructors

        #region Public Properties

        /// <summary>
        /// number of polygons per material
        /// </summary>
        public int CountMaterials
        {
            get
            {
                return this.materialNames.Count;
            }
        }

        /// <summary>
        /// faces
        /// </summary>
        public List<Polygon> Faces
        {
            get
            {
                return this.faces;
            }
        }

        /// <summary>
        /// Material names
        /// </summary>
        public string[] MaterialNames
        {
            get
            {
                return this.materialNames.ToArray();
            }
        }

        /// <summary>
        /// uv coordinates
        /// </summary>
        public List<Vector2> UVCoordinates
        {
            get
            {
                return this.uvCoordinates;
            }
        }

        /// <summary>
        /// Vectors
        /// </summary>
        public List<Vector3> Vectors
        {
            get
            {
                return this.vectors;
            }
        }

        #endregion Public Properties

        #region Public Methods

        /// <summary>
        /// Loads the new Mesh from a file
        /// </summary>
        /// <param name="filename">the pat to the mesh</param>
        /// <returns></returns>
        public static WavefrontObjects FromFile(string filename)
        {
            if (!File.Exists(filename))
            {
                return new WavefrontObjects(new List<Vector3>(), new List<Vector2>(), new List<Polygon>(), new List<string>());
            }

            List<Vector3> vectors = new List<Vector3>();
            List<Vector2> uvCoordinates = new List<Vector2>();
            List<Polygon> faces = new List<Polygon>();
            List<string> materialNames = new List<string>();
            int currentMaterial = 0;

            float scale = 1.0f;
            int nFace = 0;
            int nVertices = 0;
            int nUVCoordinates = 0;
            int nMaterials = 0;
            int nMisc = 0;

            FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read);
            FileStream fswrite = new FileStream("tes.txt", FileMode.Create, FileAccess.Write);
            StreamReader sr = new StreamReader(fs);
            StreamWriter sw = new StreamWriter(fswrite);

            while (!sr.EndOfStream)
            {
                int lineNumber = 0;
                string test = sr.ReadLine();
                sw.WriteLine(test);

                string[] splittet = test.Split(' ');
                try
                {
                    lineNumber++;
                    switch (splittet[0])
                    {
                        case "v":
                            if (splittet[1] != "")
                            {
                                Vector3 vertex = Vector3.Zero;
                                vertex.X = -ConvertToFloat(splittet[1]) * scale;
                                vertex.Y = ConvertToFloat(splittet[2]) * scale;
                                vertex.Z = ConvertToFloat(splittet[3]) * scale;
                                vectors.Add(vertex);
                            }
                            else
                            {
                                Vector3 vertex = Vector3.Zero;
                                vertex.X = -ConvertToFloat(splittet[2]) * scale;
                                vertex.Y = ConvertToFloat(splittet[3]) * scale;
                                vertex.Z = ConvertToFloat(splittet[4]) * scale;
                                vectors.Add(vertex);
                            }
                            nVertices++;
                            break;

                        case "f":
                            List<int> indices = new List<int>();
                            List<int> indicesUV = new List<int>();
                            for (int f = splittet.Length - 1; f > 0; f--)
                            {
                                string[] splitFace = splittet[f].Split('/');
                                if (splitFace[0] != "")
                                {
                                    indices.Add(Convert.ToInt32(splitFace[0]) - 1);
                                    if (splitFace.Length > 1)
                                    {
                                        int indexUV = Convert.ToInt32(splitFace[1]);
                                        if (indexUV > 0)
                                        {
                                            indicesUV.Add(indexUV - 1);
                                        }
                                        else
                                        {
                                            //in Lightwave they are negative
                                            indicesUV.Add(faces.Count * 3 + indexUV + 3);
                                        }
                                    }
                                    else
                                    {
                                    }
                                }
                            }
                            faces.Add(new Polygon(indices, currentMaterial, indicesUV));

                            nFace++;
                            break;

                        case "vt":
                            nUVCoordinates++;
                            if (splittet[1] != "")
                            {
                                Vector2 uvCoordinate = Vector2.Zero;
                                uvCoordinate.X = ConvertToFloat(splittet[1]);
                                uvCoordinate.Y = -ConvertToFloat(splittet[2]);
                                uvCoordinates.Add(uvCoordinate);
                            }
                            else
                            {
                                Vector2 uvCoordinate = Vector2.Zero;
                                uvCoordinate.X = ConvertToFloat(splittet[2]);
                                uvCoordinate.Y = -ConvertToFloat(splittet[3]);
                                uvCoordinates.Add(uvCoordinate);
                            }
                            break;

                        case "usemtl":
                            if (splittet.Length == 2)
                            {
                                string material = splittet[1];
                                bool isMaterialInArray = false;
                                for (int i = 0; i < materialNames.Count; i++)
                                {
                                    if (materialNames[i] == material)
                                    {
                                        isMaterialInArray = true;
                                        currentMaterial = i;
                                    }
                                }
                                if (!isMaterialInArray)
                                {
                                    currentMaterial = materialNames.Count;
                                    materialNames.Add(material);
                                }
                            }
                            else
                            {
                                MessageBox.Show("Error in Reading <usemtl> in WavefrontObject-file!!");
                            }
                            nMaterials++;
                            break;

                        default:
                            nMisc++;

                            break;
                    }
                }
                catch (Exception e)
                {
                    MessageBox.Show("Error in line: " + lineNumber + "\n\n Please send the Obj-file to \nreichle@planet-interkom.de");
                    MessageBox.Show(e.ToString());
                }
            }
            sr.Close();
            sw.Close();
            fswrite.Close();
            fs.Close();

            if (materialNames.Count == 0)
            {
                materialNames.Add("No Texutures");
            }

            return new WavefrontObjects(vectors, uvCoordinates, faces, materialNames);
        }

        #endregion Public Methods

        #region Private Methods

        private static float ConvertToFloat(string p)
        {
            if (p == "")
            {
                return 0.0f;
            }

            float returnValue = 0.0f;
            try
            {
                if (!float.TryParse(p, System.Globalization.NumberStyles.Float, System.Globalization.NumberFormatInfo.InvariantInfo, out returnValue))
                {
                    MessageBox.Show("Error in Casting this string: " + p);
                }
            }
            catch (Exception e)
            {
                MessageBox.Show(e.ToString());
            }

            return returnValue;
        }

        #endregion Private Methods

        #region Internal Structs

        /// <summary>
        /// One polygon
        /// </summary>
        internal struct Polygon
        {
            #region Private Fields

            private List<int> faceIndices;
            private List<int> indicesUV;
            private int materialName;

            #endregion Private Fields

            #region Public Constructors

            /// <summary>
            /// Creates a new Polygon
            /// </summary>
            /// <param name="indices"></param>
            /// <param name="materialIndex"></param>
            /// <param name="indicesUV"></param>
            public Polygon(List<int> indices, int materialIndex, List<int> indicesUV)
            {
                this.faceIndices = indices;
                //this.faceIndices.();
                this.materialName = materialIndex;
                this.indicesUV = indicesUV;
            }

            #endregion Public Constructors

            #region Public Properties

            public List<int> Indices
            {
                get
                {
                    return this.faceIndices;
                }
            }

            public List<int> IndicesUV
            {
                get
                {
                    return this.indicesUV;
                }
            }

            public int MaterialID
            {
                get
                {
                    return this.materialName;
                }
            }

            #endregion Public Properties
        }

        #endregion Internal Structs
    }
}